/*
*  Copyright 2012 m4f10
*
*  This file is part of uScan.
*  uScan is free software: you can redistribute it and/or modify it under the
*  terms of the GNU General Public License as published by the 
*  Free Software Foundation, either version 3 of the License, or (at your option)
*  any later version.
*
*    uScan is distributed in the hope that it will be useful, 
*    but WITHOUT ANY WARRANTY; without even the implied warranty of 
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
* 
*    See the GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License 
*    along with uScan. If not, see http://www.gnu.org/licenses/.
*/
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package uscan;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.security.Security;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author m4f10
 */
public class UScanCore {

    // Settings 
    private String cookie;
    private String useragent;
    private String injectedValue;
    private String dynamicPageRegExp;
    
    // Page related data
    private URL url;
    protected String original_src;
    private String[] wordlst;
    private String prefix; // prefix a utiliser pour ajouter des parametres
    
    // Others..
    private UScanThread[] thread;
    private UScanProgress  prog;
    private UScanLog    log;
    private boolean[] thrd_found;
    private String[] thrd_param;
    
    
    private int    numThread;
    
    protected int current_op;
    private final int THREAD_IDLE = 0;
    private final int THREAD_VERIFY_REG = 1;
    private final int THREAD_VERIFY_VAL = 2;
    private final int THREAD_VERIFY_ARR = 3;
    
    private int total_ins;       // Nombre total d'instruction a faire
    private int current_ins;     // Instruction en cour de traitement
    
    
    UScanCore()
    {
        this.prog = null;       // Est mis a null car optionnel
        this.log = null;
        System.setProperty("http.agent", "");
        // Pour gérer le ssl
        System.setProperty("java.protocol.handler.pkgs",
        "com.sun.net.ssl.internal.www.protocol");   
        Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
        
        
    }

    
    
    /******************************************************************
    *
    *
    *                           GETTERS AND SETTERS
    * 
    * 
    * *****************************************************************/
    
    /*
     * Getters
     */
    
    public URL getTargetURI()
    {
        return this.url;
    }
    public String[] getDictionnary()
    {
        return this.wordlst;
    }
    public UScanProgress getUScanProgress()
    {
        return this.prog;
    }
    
    /*
     * Setters
     */
    
    
    
    // Public
    
    public void setUScanLog(UScanLog log)
    {
        this.log = log;
    }
    
    public void setUScanProgress(UScanProgress prog)
    {
        this.prog = prog;
    }
    
    public void setUScanSettings(UScanSettings settings)
    {
        this.injectedValue=     settings.getDefaultInjectedParamValue();
        this.dynamicPageRegExp= settings.getDynamicPageRegExp();
        this.numThread =        settings.getNumThread();
        this.useragent =        settings.getDefaultUserAgent();
        this.cookie =           settings.getCookieValue();
    }
    
    public void setTargetURI(String uri)
    {
        URL link;
        String url = new String();
        
        
    if(!uri.isEmpty())
    {
    try {
           
        
        // Si ya pas de http:// avant on y en met un
            if(!uri.matches("http://(.*)") && !uri.matches("https://(.*)"))
            {
                  url = "http://"+uri;
            }
            else 
            {
                  url = uri;
            }
        // Si la fin de l'adresse ne designe pas un fichier et n'a pas de /
            if(!url.matches("http://(.*)/(.*)?") && 
                    !url.matches("https://(.*)/(.*)?"))
            {
                  url = url+"/";
            }
                this.url = new URL(url);
    
           // On set le prefix
                if(url.matches("http://(.*)/(.*)\\?(.*)") | 
                        url.matches("https://(.*)/(.*)\\?(.*)"))
                {
                    this.prefix = "&";
                }
                else
                {
                    this.prefix = "?";
                }
    
    } catch (MalformedURLException ex) {
        Logger.getLogger(UScanCore.class.getName()).log(Level.SEVERE, null, ex);
    }

    }
    }
    
    public void setDictionnary (String in)
    { 
      
           FileInputStream f_in;
           DataInputStream d_in;
           BufferedReader b_in;
           
        if(!in.isEmpty())
        {  
            try {
              // Ouverture du stream de lecture  
                f_in = new FileInputStream(in);
                d_in = new DataInputStream(f_in);
                b_in = new BufferedReader(new InputStreamReader(d_in));
                if((this.wordlst = this.loadDictionnary(b_in))==null)
                {
                   this.logThis("Error! It seems your dictionnary is not available!");
                }
            } catch (FileNotFoundException ex) {
                this.logThis("Error! It seems your dictionnary is not available!");
            }
        }
        
    }
    
    
    private void setThreadCurrentOp(int op)
    {
        
        if(op == this.THREAD_IDLE || op == this.THREAD_VERIFY_ARR ||
           op == this.THREAD_VERIFY_REG || op == this.THREAD_VERIFY_VAL)
        {
            this.current_op = op;
        }
    }
    
    
    /******************************************************************
    *
    *
    *                      PUBLIC FUNCTIONS/METHODS
    * 
    * 
    * *****************************************************************/
    

    // Public
    
    public String logThisEvent(String event)
    {
        System.out.println("LOG::"+System.currentTimeMillis()+"::: "
                   +event);
        return "LOG::"+System.currentTimeMillis()+"::: "
                   +event+"\n";
    }

    public void logThis(String log)
    {
        if(this.log != null)
        {
            this.log.printLog(log);
        }
    }
    
    public void clearUScanLog()
    {
        if(this.log != null)
        {
            this.log.clearLog();
        }
    }
            
    public boolean isWebPageStatic()
    {

            String first;
            String second;
            
            first = this.loadWebPage();
            second = this.loadWebPage();
            
            if(first.equalsIgnoreCase(second))
            {
                return true;

            }
            else
            {
                this.logThis("Error! This page is not static!"+
                             " It is then impossible to scan it!");
                this.prog.forceFinish();
            }
      
        return false;
    }
    
    public boolean VerifyGet(String prm)
    {
        if(this.loadWebPage(this.appendGETParameter(prm)).
                            equals(this.loadWebPage()))
        {
            return false;
        }
        return true;
    }
    
    public boolean VerifyGet(String src, String prm)
    {
        if(this.loadWebPage(appendGETParameter(prm)).equals(src))
        {
            return false;
        }
        return true;
    }
    
    public boolean VerifyGetWithValue(String prm)
    {
        if(this.loadWebPage(this.appendGETParameter(prm,this.injectedValue)).
                            equals(this.loadWebPage()))
        {
            return false;
        }
        return true;
    }
    
    public boolean VerifyGetWithValue(String src, String prm)
    {
        if(this.loadWebPage(this.appendGETParameter(prm,this.injectedValue)).
                            equals(src))
        {
            return false;
        }
        return true;
    }
    
    public boolean VerifyGetWithArray(String prm)
    {
        if(this.loadWebPage(this.appendGETParameter(prm+"[]")).
                            equals(this.loadWebPage()))
        {
            return false;
        }
        return true;
    }
    
    public boolean VerifyGetWithArray(String src, String prm)
    {
        if(this.loadWebPage(this.appendGETParameter(prm+"[]")).
                            equals(src))
        {
            return false;
        }
        return true;
    }
    
    
    public String[] GETScan()
    {
        String result = new String();
        int i;
        original_src = "";
        original_src = this.loadWebPage();
         // On set le type d'operation pour notre thread
        this.setThreadCurrentOp(this.THREAD_VERIFY_REG);
        this.initThreading();
        
       
        
        if(isWebPageStatic())
        {
            this.logThis("Results for Regular scan: ");
        for(i=0; i<getDictionnary().length-this.numThread; i+=this.numThread)
        {
            this.initThreading();
            for(int j=0; j<this.numThread; j++)
            {
                thread[j].main(this,this.wordlst[i+j]);
            }
            
            for(int j=0; j<this.numThread; j++)
            {
                try {
                    this.thread[j].thread.join();
                } catch (InterruptedException ex) {
                    Logger.getLogger(UScanCore.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            
            for(int j=0; j<this.numThread; j++)
            {
              if(thread[j].thrd_found)
                {
                    this.logThis("Found keyword: "+this.getDictionnary()[i+j]);
                    result += this.getDictionnary()[i+j]+" ";
                } 
            } 
            
            // On log la progression
            if(this.getUScanProgress()!=null)
            {
                this.getUScanProgress().addToCurrentInstruction(this.numThread);
            }
            
        }
        if((getDictionnary().length-i)>0 && (getDictionnary().length-i)<this.numThread)
        {
            
            for(int j=i; j<(getDictionnary().length); j++)
            {
                if(VerifyGet(getDictionnary()[j]))
                {
                    this.logThis("Found keyword: "+this.getDictionnary()[j]);
                    result += this.getDictionnary()[j]+" ";
                }
                // On log la progression
                if(this.getUScanProgress()!=null)
                {
                    this.getUScanProgress().addToCurrentInstruction(1);
                }
            }
        }
        return result.split(" ");
        }
        return null;
    }
    
    public String[] GETWithValueScan()
    {
        String result = new String();
        int i;
        original_src = "";
        original_src = this.loadWebPage();
         // On set le type d'operation pour notre thread
        this.setThreadCurrentOp(this.THREAD_VERIFY_VAL);
        this.initThreading();
        
       
        
        if(isWebPageStatic())
        {
           this.logThis("Results for Value Injection scan: ");
        for(i=0; i<getDictionnary().length-this.numThread; i+=this.numThread)
        {
            this.initThreading();
            for(int j=0; j<this.numThread; j++)
            {
                thread[j].main(this,this.wordlst[i+j]);
            }
            
            for(int j=0; j<this.numThread; j++)
            {
                try {
                    this.thread[j].thread.join();
                } catch (InterruptedException ex) {
                    Logger.getLogger(UScanCore.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            
            for(int j=0; j<this.numThread; j++)
            {
              if(thread[j].thrd_found)
                {
                    this.logThis("Found keyword: "+this.getDictionnary()[i+j]);
                    result += this.getDictionnary()[i+j]+" ";
                } 
            } 
            // On log la progression
            if(this.getUScanProgress()!=null)
            {
                this.getUScanProgress().addToCurrentInstruction(this.numThread);
            }
            
        }
        if((getDictionnary().length-i)>0 && (getDictionnary().length-i)<this.numThread)
        {
            
            for(int j=i; j<(getDictionnary().length); j++)
            {
                if(VerifyGetWithValue(getDictionnary()[j]))
                {
                    this.logThis("Found keyword: "+this.getDictionnary()[j]);
                    result += this.getDictionnary()[j]+" ";
                }
                // On log la progression
                if(this.getUScanProgress()!=null)
                {
                    this.getUScanProgress().addToCurrentInstruction(1);
                }
            }
        }
        return result.split(" ");
        }
        return null;
    }
    
    public String[] GETWithArrayScan()
    {
        String result = new String();
        int i;
        original_src = "";
        original_src = this.loadWebPage();
         // On set le type d'operation pour notre thread
        this.setThreadCurrentOp(this.THREAD_VERIFY_ARR);
        this.initThreading();
        
       
        
        if(isWebPageStatic())
        {
            this.logThis("Results for Array Injection scan: ");
        for(i=0; i<=getDictionnary().length-this.numThread; i+=this.numThread)
        {
            this.initThreading();
            for(int j=0; j<this.numThread; j++)
            {
                thread[j].main(this,this.wordlst[i+j]);
            }
            
            for(int j=0; j<this.numThread; j++)
            {
                try {
                    this.thread[j].thread.join();
                } catch (InterruptedException ex) {
                    Logger.getLogger(UScanCore.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            
           
            for(int j=0; j<this.numThread; j++)
            {
              if(thread[j].thrd_found)
                {
                    this.logThis("Found keyword: "+this.getDictionnary()[i+j]);
                    result += this.getDictionnary()[i+j]+" ";
                } 
            } 
            // On log la progression
            if(this.getUScanProgress()!=null)
            {
                this.getUScanProgress().addToCurrentInstruction(this.numThread);
            }
            
        }
        if((getDictionnary().length-i)>0 && (getDictionnary().length-i)<this.numThread)
        {
            
            for(int j=i; j<(getDictionnary().length); j++)
            {
                if(VerifyGetWithArray(getDictionnary()[j]))
                {
                    this.logThis("Found keyword: "+this.getDictionnary()[j]);
                    result += this.getDictionnary()[j]+" ";
                }
                // On log la progression
                if(this.getUScanProgress()!=null)
                {
                    this.getUScanProgress().addToCurrentInstruction(1);
                }
            }
        }
        return result.split(" ");
        }
        return null;
    }
    
    public void killThreads()
    {
        for(int i=0; i<this.numThread; i++)
        {
            // Theoriquement pas safe quand on modifie une var avec,
            // Ce qui n'est pas le cas ici!
            this.thread[i].thread.stop();
        } 
        try {
            this.finalize();
        } catch (Throwable ex) {
            Logger.getLogger(UScanCore.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /******************************************************************
    *
    *
    *                     PRIVATE FUNCTIONS/METHODS
    * 
    * 
    * *****************************************************************/
    
    private String[] loadDictionnary(BufferedReader buf)
    {
    try {
            String tmp;
            String fileContent = new String();

            // On load le contenu du fichier en memoire
            
            while((tmp = buf.readLine())!=null)
            { fileContent += tmp;   }
            
            // On met les keywords dans un tableau
            
            return fileContent.replaceAll("(<WORD.*?>)(.*?)(<\\/WORD.*?>)", "$2 ").split(" ");
            
            
    } catch (IOException ex) {
        Logger.getLogger(UScanCore.class.getName()).log(Level.SEVERE, null, ex);
    }
        return null;
    }

    
    private String loadWebPage()
    {
        
        BufferedReader in = null;
    try {
            String tmp;
            String page = new String();
            URLConnection con = this.getTargetURI().openConnection();
            
            if(!this.useragent.equals(""))
            {
                con.setRequestProperty("User-Agent", this.useragent);
            }
            if(!this.cookie.equals(""))
            {
                con.setRequestProperty("Cookie", this.cookie);
            }
            in = new BufferedReader(
                    new InputStreamReader(con.getInputStream()));
            // Get the content of the web page
            while((tmp = in.readLine())!=null) {
                page+=tmp;
            }
            in.close();
            return page.replaceAll(this.dynamicPageRegExp,"");
            //return page;
    } catch (IOException ex) {
        return ex.getMessage();     // Peux sembler etrange mais permet d'eviter
                                    // que sa crash...en retournant le 
                                    // message d'erreur comme etant le contenu..
    }
    }
    
    private String loadWebPage(URL uri)
    {
        BufferedReader in = null;
    try {
            URLConnection con = uri.openConnection();
            if(!this.useragent.equals(""))
            {
                con.setRequestProperty("User-Agent", this.useragent);
            }
            if(!this.cookie.equals(""))
            {
                con.setRequestProperty("Cookie", this.cookie);
            }
            String tmp;
            String page = new String();
            in = new BufferedReader(
     new InputStreamReader(con.getInputStream()));
            // Get the content of the web page
            while((tmp = in.readLine())!=null) {
                page+=tmp;
            }
            in.close();
            return page.replaceAll(this.dynamicPageRegExp,"");
            //return page;
    } catch (IOException ex) {
        return ex.getMessage();
    } 
    }
    
    
    private URL appendGETParameter(String prm) 
    {
            URL uri;
    try {
            
            uri = new URL(
                    this.getTargetURI().getProtocol()+"://"+this.getTargetURI().getHost()+
                    this.getTargetURI().getFile()+
                    this.prefix+prm+"=");
            this.logThisEvent(this.getTargetURI().getProtocol()+"://"+this.getTargetURI().getHost()+
                    this.getTargetURI().getFile()+
                    this.prefix+prm+"=");
            return uri;

        } catch (MalformedURLException ex) {
            Logger.getLogger(UScanCore.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }
    // Si on veux un parametre avec valeur
    private URL appendGETParameter(String prm, String value) 
    {
            URL uri;
    try {
            
            uri = new URL(
                    this.getTargetURI().getProtocol()+"://"+this.getTargetURI().getHost()+
                    this.getTargetURI().getFile()+
                    this.prefix+prm+"="+value);
            this.logThisEvent(this.getTargetURI().getProtocol()+"://"+this.getTargetURI().getHost()+
                    this.getTargetURI().getFile()+
                    this.prefix+prm+"="+value);
            return uri;

        } catch (MalformedURLException ex) {
            Logger.getLogger(UScanCore.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }
    
    
    
    
    
    private void initThreading()
    {
        thread = new UScanThread[this.numThread];
        thrd_found = new boolean[this.numThread];
        thrd_param = new String[this.numThread];

        for(int i=0; i<this.numThread; i++)
        {
            thread[i] = new UScanThread();
            
            thrd_found[i] = false;
            thrd_param[i] = new String();
        }
    }
 
}
